#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

#include "../rdistrolib/distributionsession.h"
#include "../rdistrolib/jobtypes.h"

using namespace RDistro;

void RunProc(DistributionSession *session, const char *exeName, const char *args)
{
	CHAR xBuf[10000];
	sprintf(xBuf, "\"%s\" %s", exeName, args);

	session->SynchronizeFile(exeName, false, true);

	PROCESS_INFORMATION procInfo;
	STARTUPINFO startInfo;
	memset(&startInfo, 0, sizeof(startInfo));
	BOOL procStarted = CreateProcess(exeName, xBuf, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &startInfo, &procInfo);

	if(!procStarted)
	{
		fprintf(stderr, "Attempt to start %s failed\n", exeName);
		return;
	}

	DWORD exitCode = STILL_ACTIVE;
	while(exitCode == STILL_ACTIVE)
	{
		Sleep(100);
		session->ProcessData();
		GetExitCodeProcess(procInfo.hProcess, &exitCode);
	}
	printf("Job terminated\n");
}

void CreateHexKey(char hexKey[65], const UInt8 *binKey)
{
	for(int i=0;i<64;i++)
	{
		int v;
		if(i & 1)
			v = (binKey[i/2] & 0xf);
		else
			v = ((binKey[i/2] & 0xf0) >> 4);
		if(v < 0xa)
			hexKey[i] = '0' + v;
		else
			hexKey[i] = 'a' + v - 0xa;
	}
	hexKey[64] = '\0';
}

void WorkOnJob(DistributionSession *session, UInt16 protocolCode, const char *target, const char *dserver, const char *dkey, const DistributionSession::JobKey &jkey)
{
	char jobKeyHex[65];
	CreateHexKey(jobKeyHex, jkey.data);

	switch(protocolCode)
	{
	case JobTypes::Lighting_Direct:
		{
			char str[8000];
			session->SynchronizeFile("rdxclasses\\Core.rxt", false, true);
			session->SynchronizeFile("rdxclasses\\TDPTools.zonepolicy", false, true);
			session->SynchronizeFile("rdxclasses\\TDP\\Math.rxt", false, true);
			session->SynchronizeFile("rdxclasses\\TDP\\Resources.rxt", false, true);

			sprintf(str, "rdxclasses\\TDP\\Resources\\Maps\\%s.rxt", target);
			session->SynchronizeFile(str, false, true);

			sprintf(str, "rphoton -castdirect -distributeslave \"%s\" \"%s\" \"%s\" \"%s\"", dserver, dkey, jobKeyHex, target);
			RunProc(session, "..\\tools\\rtoolbox" VERSION_TAG ".exe", str);
		}
		break;
	};
}

void SelfUpdate(DistributionSession *session, const char *server, const char *key)
{
	bool updatedEXE = false;
	bool updatedDLL = false;
	bool updatedLauncher = false;

	printf("Checking for updates...\n");
	session->SynchronizeFile("..\\tools\\rdistroslave" VERSION_TAG ".exe", false, true, &updatedEXE);
	session->SynchronizeFile("..\\tools\\rdistrolib" VERSION_TAG ".dll", false, true, &updatedDLL);
	session->SynchronizeFile("..\\tools\\rdistrolaunch" VERSION_TAG ".exe", false, true, &updatedLauncher);

	if(updatedEXE || updatedDLL || updatedLauncher)
	{
		printf("Slave: %s\n", updatedEXE ? "Updated" : "Old");
		printf("DistroLib: %s\n", updatedDLL ? "Updated" : "Old");
		printf("Launcher: %s\n", updatedLauncher ? "Updated" : "Old");
		printf("Distribution executables were updated.\nExiting to slave launcher in 10 seconds\n");
		DestroyDistributionSession(session);
		Sleep(5000);

		CHAR xBuf[10000];
		sprintf(xBuf, "\"..\\tools\\rdistrolaunch" VERSION_TAG ".exe\" \"..\\tools\\rdistro\\rdistroslave" VERSION_TAG ".exe\" \"%s\" \"%s\"", server, key);

		PROCESS_INFORMATION procInfo;
		STARTUPINFO startInfo;
		memset(&startInfo, 0, sizeof(startInfo));
		CreateProcess("..\\tools\\rdistrolaunch" VERSION_TAG ".exe", xBuf, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &startInfo, &procInfo);
		
		exit(0);
	}
}

int main(int argc, const char **argv)
{
	int selfUpdateCountdown = 0;

	srand(static_cast<unsigned int>(time(NULL)));

	if(argc < 3)
	{
		fprintf(stderr, "Usage: rdistroslave <hostname> <login key> [-restart]");
		return -1;
	}

	printf("*** RDISTRO DISTRIBUTION CLIENT ***\n");
	
	while(true)
	{
		DistributionSession *session = CreateDistributionSession_StringKey(argv[1], argv[2]);
		while(!session->LoggedIn() && session->ConnectionActive())
		{
			Sleep(10);
			session->ProcessData();
		}

		if(argc == 4 && !strcmp(argv[3], "-restart"))
		{
			printf("Synchronizing executables and restarting server...\n");
			session->SynchronizeFile("..\\tools\\rdistroslave" VERSION_TAG ".exe", true);
			session->SynchronizeFile("..\\tools\\rdistrolaunch" VERSION_TAG ".exe", true);
			session->SynchronizeFile("..\\tools\\luax51" VERSION_TAG ".exe", true);
			session->SynchronizeFile("..\\tools\\rdistrolib" VERSION_TAG ".dll", true);
			session->SynchronizeFile("..\\tools\\rdistroserver.lua", true);
			session->SynchronizeFile("..\\tools\\rdistro.lua", true);
			session->SynchronizeFile("..\\tools\\sockets.lua", true);
			session->SendPacket(SV_SELF_UPDATE, NULL, 0);
			DestroyDistributionSession(session);
			printf("Done sending restart request\n");
			return 0;
		}

		while(session->LoggedIn() && session->ConnectionActive())
		{
			DistributionSession::JobKey job;
			char target[4096];

			if(selfUpdateCountdown == 0)
			{
				SelfUpdate(session, argv[1], argv[2]);
				selfUpdateCountdown = 120;
			}

			if(session->RequestJob(job, target))
			{
				SelfUpdate(session, argv[1], argv[2]);
				WorkOnJob(session, job.jobTypeID, target, argv[1], argv[2], job);
				Sleep(1000);
			}
			else
				Sleep(1000);
			selfUpdateCountdown--;
		}
		
		fprintf(stderr, "Server dropped connection.  Reconnecting in 5 seconds...\n");
		Sleep(5000);

		DestroyDistributionSession(session);
	}

	return 0;
}
